#if !defined(BLOCK_QUEUE_H)
#define BLOCK_QUEUE_H

#include "locker.h"
#include <iostream>
#include <stdlib.h>
#include <sys/time.h>

/// @brief 阻塞队列类
/// @tparam T
template <class T>
class block_queue
{
private:
    locker *q_lock;
    cond *q_cond;
    T *q_array;
    int q_size;
    int q_max_size;
    int q_front;
    int q_back;

public:
    block_queue(int max_size = 1000);
    ~block_queue();

    void clear();
    bool full() const;
    bool empty() const;
    bool front(T &value) const;
    bool back(T &value) const;
    int size() const;
    int max_size() const;

public:
    /// @brief 添加元素（生产元素）
    /// @param item
    /// @return
    /// @note 先将所有使用队列的线程唤醒，若无线程等待条件变量则无意义
    bool push(const T &item);

    /// @brief 移除元素
    /// @param item
    /// @return
    bool pop(T &item);

    /// @brief 移除元素（超时处理）
    /// @param item
    /// @param ms_timeout
    /// @return
    bool pop(T &item, int ms_timeout);
};

template <class T>
block_queue<T>::block_queue(int max_size)
{
    if (max_size <= 0)
        exit(-1);
    q_max_size = max_size;
    q_array = new T[max_size];
    q_size = 0;
    q_front = -1;
    q_back = -1;

    q_lock = new locker();
    q_cond = new cond();
}

template <class T>
block_queue<T>::~block_queue()
{
    q_lock->lock();
    if (q_array != nullptr)
        delete[] q_array;
    q_lock->unlock();

    delete q_lock;
    delete q_cond;
}

template <class T>
void block_queue<T>::clear()
{
    q_lock->lock();
    q_size = 0;
    q_front = -1;
    q_back = -1;
    q_lock->unlock();
}

template <class T>
bool block_queue<T>::full() const
{
    q_lock->lock();
    if (q_size >= q_max_size)
    {
        q_lock->unlock();
        return true;
    }
    q_lock->unlock();
    return false;
}

template <class T>
bool block_queue<T>::empty() const
{
    q_lock->lock();
    if (q_size == 0)
    {
        q_lock->unlock();
        return true;
    }
    q_lock->unlock();
    return false;
}

template <class T>
bool block_queue<T>::front(T &value) const
{
    q_lock->lock();
    if (0 == q_size)
    {
        q_lock->unlock();
        return false;
    }
    value = q_array[q_front];
    q_lock->unlock();
    return true;
}

template <class T>
bool block_queue<T>::back(T &value) const
{
    q_lock->lock();
    if (0 == q_size)
    {
        q_lock->unlock();
        return false;
    }
    value = q_array[q_back];
    q_lock->unlock();
    return true;
}

template <class T>
int block_queue<T>::size() const
{
    int tmp = 0;
    q_lock->lock();
    tmp = q_size;
    q_lock->unlock();
    return tmp;
}

template <class T>
int block_queue<T>::max_size() const
{
    int tmp = 0;
    q_lock->lock();
    tmp = q_max_size;
    q_lock->unlock();
    return tmp;
}

template <class T>
bool block_queue<T>::push(const T &item)
{
    q_lock->lock();
    if (q_size >= q_max_size)
    {
        q_cond->broadcast();
        q_lock->unlock();
        return false;
    }
    q_back = (q_back + 1) % q_max_size;
    q_array[q_back] = item;
    q_size++;
    q_cond->broadcast();
    q_lock->unlock();
    return true;
}

template <class T>
bool block_queue<T>::pop(T &item)
{
    q_lock->lock();
    while (q_size <= 0)
    {
        if (!q_cond->wait(q_lock->getMutex()))
        {
            q_lock->unlock();
            return false;
        }
    }
    q_front = (q_front + 1) % q_max_size;
    item = q_array[q_front];
    q_size--;
    q_lock->unlock();
    return true;
}

template <class T>
bool block_queue<T>::pop(T &item, int ms_timeout)
{
    timespec t{0, 0};
    timeval now{0, 0};
    gettimeofday(&now, NULL);
    q_lock->lock();
    if (q_size <= 0)
    {
        t.tv_sec = now.tv_sec + ms_timeout / 1000;
        t.tv_nsec = (ms_timeout % 1000) * 1000;
        if (!q_cond->timedwait(q_lock->getMutex(), t))
        {
            q_lock->unlock();
            return false;
        }
    }
    if (q_size <= 0)
    {
        q_lock->unlock();
        return false;
    }
    q_front = (q_front + 1) % q_max_size;
    item = q_array[q_front];
    q_size--;
    q_lock->unlock();
    return true;
}

#endif // BLOCK_QUEUE_H
